/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.ohosannotations.internal.core.handler;

import com.helger.jcodemodel.AbstractJClass;
import com.helger.jcodemodel.IJExpression;
import com.helger.jcodemodel.JBlock;
import com.helger.jcodemodel.JDefinedClass;
import com.helger.jcodemodel.JFieldVar;
import com.helger.jcodemodel.JMethod;
import com.helger.jcodemodel.JVar;

import org.ohosannotations.ElementValidation;
import org.ohosannotations.OhosAnnotationsEnvironment;
import org.ohosannotations.handler.BaseAnnotationHandler;
import org.ohosannotations.helper.BundleHelper;
import org.ohosannotations.helper.CaseHelper;
import org.ohosannotations.holder.GeneratedClassHolder;

import java.lang.annotation.Annotation;

import javax.lang.model.element.Element;
import javax.lang.model.element.VariableElement;

import static com.helger.jcodemodel.JExpr.lit;
import static com.helger.jcodemodel.JMod.FINAL;
import static com.helger.jcodemodel.JMod.PUBLIC;
import static com.helger.jcodemodel.JMod.STATIC;

/**
 * 额外的参数处理程序
 *
 * @author dev
 * @since 2021-06-03
 */
public abstract class ExtraParameterHandler extends BaseAnnotationHandler<GeneratedClassHolder> {
    private Class<? extends Annotation> methodAnnotationClass;

    /**
     * 额外的参数处理程序
     *
     * @param targetClass 目标类
     * @param methodAnnotationClass 方法注释类
     * @param environment 环境
     */
    public ExtraParameterHandler(Class<? extends Annotation> targetClass,
        Class<? extends Annotation> methodAnnotationClass, OhosAnnotationsEnvironment environment) {
        super(targetClass, environment);
        this.methodAnnotationClass = methodAnnotationClass;
    }

    @Override
    protected void validate(Element element, ElementValidation valid) {
        validatorHelper.enclosingElementHasAnnotation(methodAnnotationClass, element, valid);

        validatorHelper.canBePutInABundle(element, valid);
    }

    @Override
    public void process(Element element, GeneratedClassHolder holder) throws Exception {
        // Don't do anything here.
    }

    /**
     * 得到额外的价值
     *
     * @param parameter 参数
     * @param extras 临时演员
     * @param block 块
     * @param annotatedMethod 带注释的方法
     * @param holder 持有人
     * @return {@link IJExpression}
     */
    public IJExpression getExtraValue(VariableElement parameter,
        JVar extras, JBlock block, JMethod annotatedMethod, GeneratedClassHolder holder) {
        return getExtraValue(parameter, extras, block, annotatedMethod, holder.getGeneratedClass());
    }

    /**
     * getExtraValue
     *
     * @param parameter parameter
     * @param extras extras
     * @param block block
     * @param annotatedMethod annotatedMethod
     * @param generatedClass generatedClass
     * @return decl
     */
    public IJExpression getExtraValue(VariableElement parameter,
        JVar extras, JBlock block, JMethod annotatedMethod, JDefinedClass generatedClass) {
        String parameterName = parameter.getSimpleName().toString();
        AbstractJClass parameterClass = codeModelHelper.typeMirrorToJClass(parameter.asType());

        String extraKey = getAnnotationValue(parameter);
        if (extraKey == null || extraKey.isEmpty()) {
            extraKey = parameterName;
        }

        BundleHelper bundleHelper = new BundleHelper(getEnvironment(), parameter.asType());
        IJExpression restoreMethodCall = bundleHelper.getExpressionFromBundle(parameterClass,
            extras, getStaticExtraField(generatedClass, extraKey), annotatedMethod);
        return block.decl(parameterClass, parameterName, restoreMethodCall);
    }

    /**
     * getStaticExtraField
     *
     * @param generatedClass generatedClass
     * @param extraName extraName
     * @return staticExtraField
     */
    private JFieldVar getStaticExtraField(JDefinedClass generatedClass, String extraName) {
        String staticFieldName = CaseHelper.camelCaseToUpperSnakeCase(null, extraName, "Extra");
        JFieldVar staticExtraField = generatedClass.fields().get(staticFieldName);
        if (staticExtraField == null) {
            staticExtraField = generatedClass
                .field(PUBLIC | STATIC | FINAL, getClasses().STRING, staticFieldName, lit(extraName));
        }
        return staticExtraField;
    }

    /**
     * 得到注释的值
     *
     * @param parameter parameter
     * @return null
     */
    public abstract String getAnnotationValue(VariableElement parameter);
}
